﻿using System;
using System.ComponentModel;
using System.Diagnostics;

namespace TreeListControl.Resources
{
	/// <summary>
	/// This is the abstract base class for any object that provides property change notifications.  
	/// </summary>
	public abstract class ObservableObject : INotifyPropertyChanged
	{
		#region Constructors

		protected ObservableObject()
		{
		}

		#endregion Constructors

		#region Protected Properties

		/// <summary>
		/// Returns whether an exception is thrown, or if a Debug.Fail() is used
		/// when an invalid property name is passed to the VerifyPropertyName method.
		/// The default value is false, but subclasses used by unit tests might 
		/// override this property's getter to return true.
		/// </summary>
		protected virtual bool ThrowOnInvalidPropertyName
		{
			get; private set;
		}

		#endregion Protected Properties

		#region Protected Methods

		/// <summary>
		/// Raises this object's PropertyChanged event.
		/// </summary>
		/// <param name="propertyName">The property that has a new value.</param>
		protected void RaisePropertyChanged(string propertyName)
		{
			this.VerifyPropertyName(propertyName);

			PropertyChangedEventHandler handler = this.PropertyChanged;
			if (handler != null)
			{
			    var e = new PropertyChangedEventArgs(propertyName);
			    handler(this, e);
			}
		}

		#endregion Protected Methods

		#region Public Methods

		/// <summary>
		/// Warns the developer if this object does not have
		/// a public property with the specified name. This 
		/// method does not exist in a Release build.
		/// </summary>
		[Conditional("DEBUG")]
		[DebuggerStepThrough]
		public void VerifyPropertyName(string propertyName)
		{
			// If you raise PropertyChanged and do not specify a property name,
			// all properties on the object are considered to be changed by the binding system.
			if (String.IsNullOrEmpty(propertyName))
			    return;

			// Verify that the property name matches a real,  
			// public, instance property on this object.
			if (TypeDescriptor.GetProperties(this)[propertyName] == null)
			{
			    string msg = "Invalid property name: " + propertyName;

			    if (this.ThrowOnInvalidPropertyName)
			        throw new ArgumentException(msg);
			    else
			        Debug.Fail(msg);
			}
		}

		#endregion Public Methods

		#region Events

		/// <summary>
		/// Raised when a property on this object has a new value.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		#endregion Events
	}
}